// 暴露类接口
class LazyLoad implements LazyLoadInterface{
  els: NodeListOf<HTMLImageElement>;
  options: LazyOptions = {};
  private root = window; // 监听的根元素
  private defaultOptions: LazyOptions = {
    // 默认option参数
    top: document.documentElement.clientHeight,
    left: document.documentElement.clientWidth,
    useThrottle: false,
    useDebounce: false,
    throttleTime: 200,
    debounceTime:200,
    methods: null,
  };
  static VERSION: string = "0.0.6";
  constructor(options: any) {
    if (!isObject(options)) {
      // 判断外界参数是否是对象
      console.warn("Init warn：请检查初始化参数！");
    }
    this.options = this.setOptions(options); // 初始化option参数
    // console.log(this.options);
    this.els = this.getEls();
    this.scrollDetected(); // 为根元素绑定滚动检测事件
    this.renderImg(); // 初始化时手动触发渲染方法
  }
  // 设置options参数
  protected setOptions(this: LazyLoad, options: any) {
    Object.keys(this.defaultOptions).forEach((key) => {
      // 遍历默认options中的key值并给予设定
      this.options[key] = options[key]
        ? options[key]
        : this.defaultOptions[key];
    });
    return this.options;
  }
  // 获取得到懒加载图片元素
  protected getEls() {
    return document.querySelectorAll("img[data-lazyload]") as NodeListOf<
      HTMLImageElement
    >;
  }
  // 计算触发视图条件的参数对象
  protected calView(this: LazyLoad) {
    let top: any = null;
    let left: any = null;
    const viewportHeight = document.documentElement.clientHeight;
    const viewportWidth = document.documentElement.clientWidth;
    if (
      typeof this.options.top === "number" ||
      typeof this.options.top === "string"
    ) {
      top =
        typeof this.options.top === "number"
          ? this.options.top
          : parseFloat(this.options.top) * viewportHeight;
    }
    if (
      typeof this.options.left === "number" ||
      typeof this.options.left === "string"
    ) {
      left =
        typeof this.options.left === "number"
          ? this.options.left
          : parseFloat(this.options.left) * viewportWidth;
    }
    return {
      viewY: top < viewportHeight ? top : viewportHeight,
      viewX: left < viewportWidth ? left : viewportWidth,
    };
  }
  // 检查元素是否隐藏
  protected isHidden(elem: HTMLImageElement): boolean {
    return elem.offsetParent === null;
  }
  // 检查元素是否满足给定条件范围内
  protected checkInView(elem: HTMLImageElement) {
    const rect: DOMRect = elem.getBoundingClientRect();
    const view: CurrView = this.calView();
    // console.log(rect.top);
    return rect.top < view.viewY && rect.left < view.viewX;
  }
  // 为图像元素绑定加载完成事件
  protected onloadImg(this: LazyLoad, elem: HTMLImageElement) {
    if (!this.options.methods) return; // 安全检测
    Object.keys(this.options.methods).forEach((key) => {
      const bindMethod = this.options.methods[key];
      elem.addEventListener("load", bindMethod);
    });
  }
  // 渲染图片并进行处理
  protected renderImg() {
    this.els.forEach((elem) => {
      if (this.isHidden(elem)) return; // 检测图片的display是否生效,不生效直接返回
      if(!elem.dataset.lazyload) return;// 如果元素已经懒加载过了直接返回
      if (this.checkInView(elem)) {
        this.onloadImg(elem); // 注册加载完成事件
        elem.src = elem.dataset.lazyload as string;
        elem.removeAttribute("data-lazyload");
      }
    });
  }
  // 滚动事件检测
  protected scrollDetected() {
    let _this = this; // 为后续绑定，防止丢失实例this
    if (this.options.useThrottle) {
      this.root.addEventListener(
        "scroll",
        _throttle(
          this.renderImg.bind(_this),
          this.options.throttleTime as number
        )
      );
    } else if (this.options.useDebounce) {
      this.root.addEventListener(
        "scroll",
        _debounce(
          this.renderImg.bind(_this), 
          this.options.debounceTime as number
        )
      );
    } else {
      this.root.addEventListener("scroll", this.renderImg.bind(_this));
    }
  }
}

// 判断对象参数是否成立并提供类型推断
function isObject(target: any): target is Object {
  return Object.prototype.toString.call(target) === "[object Object]";
}

// 节流控制部分
function _throttle(callback: Function, delay: number) {
  let canRun: boolean = true;
  const throttledFunc: ThrottledFunction = function(this: any, ...args: any[]) {
    let _this = this;
    if (canRun === false) {
      return;
    }
    callback.apply(_this, args);
    canRun = false;
    setTimeout(() => {
      canRun = true;
    }, delay);
  };
  throttledFunc.cancel = () => (canRun = true); // 设置取消节流方法
  return throttledFunc as any;
}

// 防抖控制部分
function _debounce(callback: Function, delay: number) {
  let timeId: TimeID = undefined;
  const debouncedFunc: DebouncedFunction = function(this: any, ...args: any[]) {
    clearTimeout(timeId);
    let _this = this;
    timeId = setTimeout(() => {
      callback.apply(_this, args);
    }, delay);
  };
  debouncedFunc.cancel = () => clearTimeout(timeId); // 设置取消防抖方法
  return debouncedFunc as any;
}
